package junior.db.dao.update;

import junior.db.dao.base.Filter;
import junior.db.dao.base.MybatisHelper;
import junior.db.dao.base.PersistenceAware;
import junior.db.dao.ext.IExtensible;
import junior.db.query.QueryHelper;

import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @author wubo
 * @date 2024/8/30 13:42
 */
public class UpdateOption extends AbstractUpdateEntityOption<UpdateOption> implements IExtensible {
    protected Object entity;
    protected Object[] updateValues;
    private Map<String, Object> ext;
    private List<String> extFields;
    
    public UpdateOption() {
        this(true);
    }
    
    public UpdateOption(boolean skipNull) {
        super(skipNull);
    }
    
    public Object[] getUpdateValues() {
        return updateValues;
    }
    
    @Override
    public void beforeExecuteNode() {
        initFields();
        initValues();
        initFieldFilters();
    }
    
    @Override
    public IExtensible doEntityExt(List<String> fields) {
        this.extFields = fields;
        return this;
    }
    
    @Override
    public IExtensible doExt(Map<String, Object> ext, List<String> fields) {
        this.extFields = fields;
        this.ext = ext;
        return this;
    }
    
    protected void initFieldFilters() {
        if (fieldFilters != null) {
            for (FieldFilter ff : fieldFilters) {
                Object value = getFieldValue(ff.field);
                if (value == null && this.isSkipNullValueFilter()) {
                    continue;
                }
                this.addFilter(new Filter(ff.field, ff.operator, ff.likeMode != null ? ff.likeMode.wrap(value) : value));
            }
        }
    }
    
    protected void initFields() {
        String[] ignoreFields = excludeFields;
        if (ignoreFields == null) {
            ignoreFields = new String[0];
        }
        if (updateFields == null) {
            updateFields = QueryHelper.getDBFieldNames(entityClass, "", ignoreFields);
        } else if (ignoreFields.length > 0 || extFields != null) {
            Set<String> excludeFieldSet = new HashSet<>(Arrays.asList(ignoreFields));
            Set<String> fields = Arrays.stream(updateFields).filter(field -> !excludeFieldSet.contains(field)).collect(Collectors.toSet());
            if (extFields != null) {
                fields.addAll(extFields);
            }
            updateFields = fields.toArray(new String[0]);
        }
    }
    
    protected void initValues() {
        updateValues = new Object[updateFields.length];
        
        for (int i=0; i<updateFields.length; i++) {
            String field = updateFields[i];
            updateValues[i] = getValue(field);
        }
    }
    
    protected Object getValue(String field) {
        String expression = fieldExpressionMap == null ? null : fieldExpressionMap.get(field);
        if (expression != null) {
            return MybatisHelper.sqlToSqlValue(namespace, entityClass, expression, f -> getFieldValue(f));
        }
        return getFieldValue(field);
    }
    
    protected Object getFieldValue(String field) {
        if (ext != null && ext.containsKey(field)) {
            return ext.get(field);
        }
        if (updateFieldValueMap != null && updateFieldValueMap.containsKey(field)) {
            return updateFieldValueMap.get(field);
        }
        return MybatisHelper.getFieldValue(entity, field);
    }
    
    void init(Class<?> entityClass, Object entity) {
        super.entityClass = entityClass;
        this.entity = entity;
        
        if (entity instanceof PersistenceAware) {
            UpdateOptionHelper.entityBeforeUpdate(this, entity);
        }
        
        if (updateFields == null) {
            updateFields = QueryHelper.getDBFieldNames(entityClass, null);
        }
    }
}
